using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Xml.Serialization;
using AntEater.ComponentModel.BuildElements;

namespace AntEater.ComponentModel
{
    /// <summary>
    /// Represents the content of a build file.
    /// </summary>
    [Serializable()]
    public class BuildFileDefinition
    {
        /// <summary>
        /// Occurs when the instance or some of his child objects has changed.
        /// </summary>
        public event EventHandler<EventArgs> Changed;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:BuildFileDefinition"/> class.
        /// </summary>
        public BuildFileDefinition(string buildFilePath) {
            this._buildFilePath = buildFilePath;

            this._rootBuildElement = this.CreateNewBuildElement(typeof(RootBuildElement)) as RootBuildElement;
            this._rootBuildElement.BuildFilePath = buildFilePath;

            this._targets = new Collection<TargetBuildElement>();
            this._targetNameChangeTracker = new PropertyChangeTracker("Name");

            this._usingTasks = new Collection<UsingTaskBuildElement>();
            this._taskNameChangeTracker = new PropertyChangeTracker("TaskName");

        }

        private IRootBuildElement _rootBuildElement;

        /// <summary>
        /// Gets or sets the root build element.
        /// </summary>
        /// <value>The root build element.</value>
        public IRootBuildElement RootBuildElement {
            get { return _rootBuildElement; }
        }

        private Collection<TargetBuildElement> _targets;

        /// <summary>
        /// Gets the targets.
        /// </summary>
        /// <value>The targets.</value>
        public Collection<TargetBuildElement> Targets {
            get { return _targets; }
        }

        private Collection<UsingTaskBuildElement> _usingTasks;

        /// <summary>
        /// Gets or sets the using tasks.
        /// </summary>
        /// <value>The using tasks.</value>
        public Collection<UsingTaskBuildElement> UsingTasks {
            get { return _usingTasks; }
        }

        private string _buildFilePath;

        /// <summary>
        /// Gets or sets the build file path.
        /// </summary>
        /// <value>The build file path.</value>
        public string BuildFilePath {
            get { return _buildFilePath; }
        }

        [NonSerialized()]
        private PropertyChangeTracker _targetNameChangeTracker;

        /// <summary>
        /// Gets the target name change tracker.
        /// </summary>
        /// <value>The target name change tracker.</value>
        [XmlIgnore()]
        internal PropertyChangeTracker TargetNameChangeTracker {
            get { return _targetNameChangeTracker; }
        }

        [NonSerialized()]
        private PropertyChangeTracker _taskNameChangeTracker;

        /// <summary>
        /// Gets the task name change tracker.
        /// </summary>
        /// <value>The task name change tracker.</value>
        [XmlIgnore()]
        internal PropertyChangeTracker TaskNameChangeTracker {
            get { return _taskNameChangeTracker; }
        }

        /// <summary>
        /// Gets the default targets.
        /// </summary>
        /// <returns></returns>
        public TargetCollection GetDefaultTargets() {
            return this.GetDefaultTargets(this.RootBuildElement.ChildElements);
        }

        /// <summary>
        /// Gets the default targets.
        /// </summary>
        /// <param name="elements">The elements.</param>
        /// <returns></returns>
        private TargetCollection GetDefaultTargets(BuildElementCollection elements) {

            TargetCollection targets = new TargetCollection(this);
            foreach (IBuildElement element in elements) {

                // Check if element is a project
                ProjectBuildElement project = element as ProjectBuildElement;
                if (project != null) {
                    // Check if project contains default Targets
                    if (project.DefaultTargets.Count > 0) {
                        targets = project.DefaultTargets;
                        break;
                    }
                }

                // Check if it is a conatiner
                IContainerBuildElement container = element as IContainerBuildElement;
                if (container != null) {
                    // Get default targets from container 
                    targets = this.GetDefaultTargets(container.ChildElements);
                }
            }

            return targets;
        }

        /// <summary>
        /// Creates the new build element.
        /// </summary>
        /// <param name="buildElementType">Type of the build element.</param>
        /// <returns></returns>
        public IBuildElement CreateNewBuildElement(Type buildElementType) {

            if (buildElementType == null) { throw new ArgumentNullException("buildElementType"); }

            IBuildElement buildElement = Activator.CreateInstance(buildElementType, this) as IBuildElement;

            RegisterBuildElement(buildElement);

            return buildElement;
        }

        /// <summary>
        /// Deletes the build element.
        /// </summary>
        /// <param name="element">The element.</param>
        public void DeleteBuildElement(IBuildElement element) {

            if (element == null) { throw new ArgumentNullException("element"); }

            IContainerBuildElement container = element as IContainerBuildElement;

            if (container != null) {
                while(container.ChildElements.Count > 0){
                    this.DeleteBuildElement(container.ChildElements[0]);
                }
            }

            IContainerBuildElement parentContainer = element.Parent as IContainerBuildElement;

            if (parentContainer != null) {
                parentContainer.ChildElements.Remove(element);
            }

            UnRegisterBuildElement(element);
        }

        /// <summary>
        /// Called when [changed].
        /// </summary>
        protected void OnChanged() {
            if (this.Changed != null) {
                this.Changed(this, new EventArgs());
            }
        }

        /// <summary>
        /// Handles the PropertyChanged Event of all BuildElements.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void BuildElementPropertyChanged(object sender, PropertyChangedEventArgs e) {

            TargetBuildElement target = sender as TargetBuildElement;
            if (target != null) {
                if (e.PropertyName == "Name") {

                }
            }

            OnChanged();
        }

        /// <summary>
        /// Performs all registration Tasks for an BuildElement.
        /// </summary>
        /// <param name="buildElement">The build element.</param>
        private void RegisterBuildElement(IBuildElement buildElement) {
            buildElement.PropertyChanged += new PropertyChangedEventHandler(BuildElementPropertyChanged);

            TargetBuildElement targetBuildElement = buildElement as TargetBuildElement;
            if (targetBuildElement != null) {
                this.Targets.Add(targetBuildElement);
                this.TargetNameChangeTracker.RegisterObject(targetBuildElement);
            }

            UsingTaskBuildElement usingTaskBuildElement = buildElement as UsingTaskBuildElement;
            if (usingTaskBuildElement != null) {
                this.UsingTasks.Add(usingTaskBuildElement);
                this.TaskNameChangeTracker.RegisterObject(usingTaskBuildElement);
            }

        }

        /// <summary>
        /// Performs all unregistration Tasks for an BuildElement.
        /// </summary>
        /// <param name="buildElement">The build element.</param>
        private void UnRegisterBuildElement(IBuildElement buildElement) {
            buildElement.PropertyChanged -= new PropertyChangedEventHandler(BuildElementPropertyChanged);

            TargetBuildElement targetBuildElement = buildElement as TargetBuildElement;
            if (targetBuildElement != null) {
                this.Targets.Remove(targetBuildElement);
                this.TargetNameChangeTracker.UnregisterObject(targetBuildElement);
            }

            UsingTaskBuildElement usingTaskBuildElement = buildElement as UsingTaskBuildElement;
            if (usingTaskBuildElement != null) {
                this.UsingTasks.Remove(usingTaskBuildElement);
                this.TaskNameChangeTracker.UnregisterObject(usingTaskBuildElement);
            }
        }
    }
}
